﻿using System;
using System.Reflection;
using System.Runtime.InteropServices.Expando;
using System.Runtime.InteropServices.ComTypes;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Net;
using System.Linq;
using System.Text;
using BrainTechLLC.ImageOperatorsNonAForgeDependent;
using BrainTechLLC;
using System.Configuration;
using System.Diagnostics;
using BrainTechLLC.ThreadSafeObjects;
using System.Windows.Forms;
using System.Windows.Threading;

namespace BrainTechLLC.WebCamLib3
{
    public class GenericVideoFeed : BaseNotifyPropertyChanged, IVideoSource, IDisposable, INotifyPropertyChanged, IVideoFeed
    {
        protected ThreadSafeQueue<FrameInfo> _incomingFrameQueue = new ThreadSafeQueue<FrameInfo>();
        protected bool _cameraCaptureRunning;
        protected string source = "Webcam";
        protected string name = "";
        protected string _cameraName = "";
        protected double _minimumTimeBetweenHardwareFrames = 0.01D;
        public int _incomingVideoWidth = -1;
        public int _incomingVideoHeight = -1;
        protected string _cameraMoniker = "No Webcam";
        protected Thread _cameraCaptureThread = null;
        public bool _isProcessing = false;
        public Exception _cameraError = null;
        protected bool _wantCameraStop;

        public ThreadSafeQueue<FrameInfo> IncomingFrameQueue
        {
            get { return _incomingFrameQueue; }
        }

        public string CameraMoniker
        {
            get { return _cameraMoniker; }
            protected set
            {
                if (_cameraMoniker != value)
                {
                    _cameraMoniker = value;
                    OnPropertyChanged("CameraMoniker");
                }
            }
        }

        public int IncomingFrameQueueCount
        {
            get { return IncomingFrameQueue.Count; }
        }

        public string CameraName
        {
            get { return _cameraName; }
            protected set
            {
                if (_cameraName != value)
                {
                    _cameraName = value;
                    OnPropertyChanged("CameraName");
                }
            }
        }

        public double MinimumTimeBetweenHardwareFrames
        {
            get { return _minimumTimeBetweenHardwareFrames; }
            set
            {
                if (_minimumTimeBetweenHardwareFrames != value)
                {
                    _minimumTimeBetweenHardwareFrames = value;
                    OnPropertyChanged("MinimumTimeBetweenHardwareFrames");
                }
            }
        }

        public int IncomingVideoWidth
        {
            get { return _incomingVideoWidth; }
            protected set
            {
                if (_incomingVideoWidth != value)
                {
                    _incomingVideoWidth = value;
                    OnPropertyChanged("IncomingVideoWidth");
                }
            }
        }

        public int IncomingVideoHeight
        {
            get { return _incomingVideoHeight; }
            protected set
            {
                if (_incomingVideoHeight != value)
                {
                    _incomingVideoHeight = value;
                    OnPropertyChanged("IncomingVideoHeight");
                }
            }
        }

        public bool IsProcessing
        {
            get { return _isProcessing; }
            protected set
            {
                if (_isProcessing != value)
                {
                    _isProcessing = value;
                    OnPropertyChanged("IsProcessing");
                }
            }
        }

        public Exception CameraError
        {
            get { return _cameraError; }
            protected set
            {
                if (_cameraError != value)
                {
                    _cameraError = value;
                    OnPropertyChanged("CameraError");
                }
            }
        }

        public ThreadPriority CameraCaptureThreadPriority
        {
            get
            {
                if (_cameraCaptureThread != null)
                    return _cameraCaptureThread.Priority;

                return ThreadPriority.Normal;
            }
            set
            {
                if (_cameraCaptureThread != null)
                {
                    try
                    {
                        _cameraCaptureThread.Priority = value;
                        OnPropertyChanged("CameraCaptureThreadPriority");
                    }
                    catch { }
                }
            }
        }

        public void Dispose()
        {
            try
            {
                StopCameraCapture();
            }
            catch { }
        }
        
        public bool CameraCaptureRunning { get { return _cameraCaptureRunning; } }        

        public FrameInfo GetNextFrame()
        {
            FrameInfo frameInfo;

            if (IncomingFrameQueue.Count > 10)
            {
                FrameInfo[] tossers = new FrameInfo[8];
                int dequeued = IncomingFrameQueue.DequeueMultiple(tossers, 8);

                for (int n = 0; n < dequeued; n++)
                {
                    FrameInfo toToss = tossers[n];
                    toToss.ClearBitmapAndBytes();
                }
            }

            if (!IncomingFrameQueue.Dequeue(out frameInfo))
            {
                return null;
            }

            OnPropertyChanged("IncomingFrameQueueCount");
            return frameInfo;
        }

        private bool AssignCameraNameAndMoniker(string cameraName, string moniker)
        {
            this.name = cameraName;
            this.CameraName = cameraName;
            this.CameraMoniker = moniker;
            this.source = moniker;
            return true;
        }

        public void ClearVideoQueue()
        {
            IncomingFrameQueue.Clear();
        }

        // Close video source
        public void StopCameraCapture()
        {
            if (_cameraCaptureThread != null)
            {
                SignalCameraToStop();
                Thread.Sleep(300);

                TerminateCameraCapture();
            }
            ClearVideoQueue();

            IncomingVideoWidth = -1;
            IncomingVideoHeight = -1;
        }

        // Start video source
        public bool StartCameraCapture()
        {
            return StartCameraCapture(ThreadPriority.Normal, _minimumTimeBetweenHardwareFrames);
        }

        // Start work
        public bool StartCameraCapture(ThreadPriority cameraCapturePriority, double minimumTimeBetweenHardwareFrames)
        {
            if (_cameraCaptureThread != null)
                return false;

            _minimumTimeBetweenHardwareFrames = minimumTimeBetweenHardwareFrames;
            _cameraCaptureThread = new Thread(new ThreadStart(WorkerThread));
            _cameraCaptureThread.IsBackground = true;
            _cameraCaptureThread.Name = source;
            _cameraCaptureThread.Priority = cameraCapturePriority;
            _cameraCaptureThread.Start();
            return true;
        }

        // Signal thread to stop work
        public void SignalCameraToStop()
        {
            if (_cameraCaptureThread != null)
            {
                _wantCameraStop = true;
            }
        }

        // Wait for thread stop
        public void WaitForCameraToStop()
        {
        }

        // Abort thread
        public void TerminateCameraCapture()
        {
            if (_cameraCaptureThread != null)
            {
                try
                {
                    _cameraCaptureThread.Abort();
                }
                catch { }
            }
        }

        private void WorkerThread()
        {
            _cameraCaptureRunning = true;
            OnPropertyChanged("CameraCaptureRunning");
            IsProcessing = true;
            CameraError = null;

            while (!_wantCameraStop)
            {
                Thread.Sleep(100);
            }

            _cameraCaptureThread = null;
            _cameraCaptureRunning = false;
            OnPropertyChanged("CameraCaptureRunning");
        }        
    }
}